<?php
/**
 * ------------------------------------
 *  文件上传 操作类
 * ------------------------------------
 * @author  Midnight(杨云洲),  yangyunzhou@foxmail.com
 * @copyright  育通天下
 */
class Upload extends Action
{
	/**
	 * 公共变量
	 */
	// 上传类型: 本地/远程/批量;
	public $uploadType;

	// 定义要处理的文件
	public $file;

	// 允许上传的扩展
	public $expandName = 'jpg,jpeg,gif,png,bmp,pdf,zip,rar,7z,gz,txt,doc,xls,dps';

	// 允许上传的文件类型
	public $type = array();

	// 最大上传限制  默认8M
	public $maxFile = 8;

	// 缩略图, 默认开启
	public $thumbStatus = true;

	// 缩略图  宽
	public $thumbWidth = 120;

	// 缩略图  高
	public $thumbHeigth = 90;

	// 保存路径
	public $savaPath;

	// 启用子目录保存文件
    public $autoSub   =  true;

	// 子目录创建方式 可以使用hash date
    public $subType   = 'date';
    public $dateFormat = 'y/md';
    public $hashLevel =  2; // hash的目录层次

    // 上传文件命名规则
    // 例如可以是 time uniqid com_create_guid 等
    // 必须是一个无需任何参数的函数名 可以使用自定义函数
    public $saveRule = 'uniqid';

    // 上传文件Hash规则函数名
    // 例如可以是 md5_file sha1_file 等
    public $hashType = 'md5_file';

   	//public $

	/**
	 * 私有变量
	 */
    private $rule;

	// 强制拦截上传扩展名
	private $forciblyHoldUp = 'php,jsp,asp,js,html,htm,shtml,css,exe,vbs,bat,shs,txt';

	// 远程下载到本地文件
	private $remoteToLocalFile;

	// 正在处理文件的临时扩展名
	private $ext;

	// 上传文件信息
	private $uploadFileInfo;

	// 上传错误信息
    public $error;


	/**
	 * ------------------------------------
	 *  验证远程上传文件大小
	 * ------------------------------------
	 * @author  Midnight(杨云洲),  yangyunzhou@foxmail.com
	 * @param $file
	 */
	private function verifyRemoteFileSize($file)
	{
		$size = get_headers($file);
		$size = $size[6];
		preg_match('/Content-Length: ([0-9].*)/i', $size, $size);
		if($size[1] > $this->maxFile) $this->error('上传出错, 超过最大上传限制 ('.$this->maxFile.'Byte)');
		return $size[1];
	}


	/**
	 * ------------------------------------
	 *  验证本地上传文件大小
	 * ------------------------------------
	 * @author  Midnight(杨云洲),  yangyunzhou@foxmail.com
	 * @param unknown_type $file
	 */
	private function verifyLocalFileSize($file)
	{
        if($file['size'] > $this->maxFile) {
        	$this->error = '上传出错, 超过最大上传限制 ('.$this->maxFile.'Byte)';
        	return false;
        }
        return $file['size'];
	}


	/**
	 * ------------------------------------
	 *  验证上传扩展名
	 * ------------------------------------
	 * @author Midnight(杨云洲), yangyunzhou@foxmail.com
	 */
	private function verifyExtensionName($info)
	{
		if(!$info) {
			$this->error = '上传出错, 无法获取上传文件的后缀';
			return false;
		}

		// 转换为小写
		$info = strtolower($info);

		// 允许上传的文件后缀
		$ext  = explode(',', $this->expandName);
		$ext  = array_unique($ext);
		$ext  = array_flip($ext);

		// 强制禁止上传的文件
		$forciblyHoldUp = array_flip(explode(',',$this->forciblyHoldUp));
		if(array_key_exists($info, $forciblyHoldUp)) {
			$this->error = '上传出错, 禁止上传 '.$this->forciblyHoldUp.'等文件';
			return false;
		} elseif(!array_key_exists($info, $ext)) {
			$this->error = '上传出错, 文件不在允许范围内('.$this->expandName.')';
			return false;
		}
		unset($info, $ext, $forciblyHoldUp);
	}


	/**
	 * ------------------------------------
	 *  根据上传文件命名规则取得保存文件名
	 * ------------------------------------
	 * @author  Midnight(杨云洲),  yangyunzhou@foxmail.com
	 * @param unknown_type $file
	 */
	private function getSaveName($file)
	{
		$rule = $this->saveRule;
		if(empty($rule)) { //没有定义规则, 保持默认文件名不变
			$saveName = $file['basename'];
		} elseif(function_exists($rule)) {
			$this->rule = $rule();
			$saveName = $this->rule.".".$file['extension'];
		}
		if($this->autoSub) {
            // 使用子目录保存文件
            $saveName   =  $this->getSubName($file).'/'.$saveName;
        }
		return $saveName;
	}


	/**
	 * ------------------------------------
	 *  获取子目录的名称
	 * ------------------------------------
	 * @author  Midnight(杨云洲),  yangyunzhou@foxmail.com
	 * @param $file 文件信息
	 */
    private function getSubName($file)
    {
        switch($this->subType) {
            case 'date': $dir = date($this->dateFormat,time()); break;
            case 'hash':
            default:
                $name = md5($file['basename']);
                $dir   =  '';
                for($i=0;$i<$this->hashLevel;$i++) {$dir .=  $name{0}.'/';}
                break;
        }
        $wDir = $this->savaPath.'/'.$dir;
        if(!is_dir($wDir)){mk_dir($wDir);}
        return $dir;
    }


    /**
     * ------------------------------------
     *  获取本地上传错误信息
     * ------------------------------------
     * @author  Midnight(杨云洲),  yangyunzhou@foxmail.com
     */
    private function getLocalError($error)
    {
        switch($error)
        {
        	case 1: return $this->error = '文件大小超出了服务器的空间大小'; break;
        	case 2: return $this->error = '要上传的文件大小超出浏览器限制'; break;
        	case 3: return $this->error = '文件仅部分被上传';             break;
        	case 4: return $this->error = '没有找到要上传的文件';          break;
        	case 5: return $this->error = '服务器临时文件夹丢失';          break;
        	case 6: return $this->error = '文件写入到临时文件夹出错';       break;
        }
    }


	/**
	 * ------------------------------------
	 *  上传一个本地文件
	 * ------------------------------------
	 * @author  Midnight(杨云洲),  yangyunzhou@foxmail.com
	 */
	private function saveLocal()
	{
		if($this->getLocalError($this->file['error'])) return false;
		$fileExt   = explode('.', $this->file['name']);
		$extlength = count($fileExt);
		$fileExt   = $fileExt[$extlength-1];

		// 验校文件信息
		if(false === $this->verifyExtensionName($fileExt)) return false;
        if(false === $this->verifyLocalFileSize($this->file)) return false;

        // 转换
        $this->file['basename'] = $this->file['name'];
        $this->file['extension'] = $fileExt;
        unset($this->file['name'], $fileExt);

        // 登记上传文件的信息
        $file = array();
        $file['name']      = $this->file['basename'];
        $file['size']      = $this->file['size'];
        $file['extension'] = $this->file['extension'];
        $file['savaName']  = $this->getSaveName($this->file);
        $file['savaPath']  = $this->savaPath;

        // 处理文件
        $url = $file['savaPath'].$file['savaName'];
        if(!move_uploaded_file($this->file['tmp_name'], $url)) {
        	$this->error = '上传失败, 请重新上传!';
        	return false;
        } else {
			// 生成缩略图
			if($this->thumbStatus == true && in_array($file['extension'], array('gif', 'jpg', 'png', 'bmp', 'jpge'))) {
				$rule = $this->savaPath.$this->getSubName($file).'/'.$this->rule.'_thumb'.'.'. $file['extension'];
				$file['rule'] = $this->getSubName($file).'/'.$this->rule.'_thumb'.'.'. $file['extension'];
				Import('Org.Util.Image');
        		Image::thumb($url, $rule, '', 120, 90);
			}
        	$file['savaPath'] = $file['savaName'];
        	$this->uploadFileInfo = $file;
        	return true;
        }
	}


	/**
	 * ------------------------------------
	 *  上传一个远程文件
	 * ------------------------------------
	 * @author  Midnight(杨云洲),  yangyunzhou@foxmail.com
	 */
	private function saveRemote()
	{
		if(!@fopen($this->file, 'r')) $this->error('无法访问远程文件');
		$remoteInfo = pathinfo($this->file);
		$this->verifyExtensionName($remoteInfo['extension']);

		// 登记上传文件的信息
		$file = array();
		$file['name']      = $remoteInfo['basename'];
		$file['size']      = $this->verifyRemoteFileSize($this->file);
		$file['extension'] = $remoteInfo['extension'];
		$file['savaName']  = $this->getSaveName($remoteInfo);
        $file['savaPath']  = $this->savaPath;

		// 处理文件
		$url = $file['savaPath'].$file['savaName'];
		$files = file_get_contents($this->file);
		$e = fopen($url, 'wb+');
		if(fwrite($e, $files) === false) $this->error('指定的文件上传出错', 'no');
		fclose($e);

		$file['savaPath']  = $url;
		$this->uploadFileInfo = $file;
	}


	/**
	 * ------------------------------------
	 *  处理上传的文件
	 * ------------------------------------
	 * @author  Midnight(杨云洲),  yangyunzhou@foxmail.com
	 */
	public function upload()
	{
		if($this->uploadType == 'remoteUpload') {// 远程上传
			return $this->saveRemote();
		} elseif($this->uploadType == 'localUpload') {// 本地上传
			return $this->saveLocal();
		}
	}


	/**
	 * ------------------------------------
	 *  获取错误消息
	 * ------------------------------------
	 * @author  Midnight(杨云洲),  yangyunzhou@foxmail.com
	 */
	public function getErrorMsg()
	{
		return $this->error;
	}


	/**
	 * ------------------------------------
	 *   获取上传文件信息
	 * ------------------------------------
	 * @author  Midnight(杨云洲),  yangyunzhou@foxmail.com
	 */
	public function getuploadFileInfo()
	{
		return $this->uploadFileInfo;
	}
}
?>